#include "vehiclePath.h"
#include <stdlib.h>
#include "main-tools.h"


/**variabile utilizzata nelle funzioni getNextEdgeInVehiclePath, getPrevEdgeInVehiclePath e getNearEdgeInVehiclePath.
 * Questa variabile (il cui uso e' <b>RISERVATO</b>) permette a queste funzioni di mantenere memoria di dove l'utente
 * era rimasto a scandagliare il percorso. Siccome la variabile e' una sola se si passa da un VehiclePath ad un altro e' caldamente
 * congliato di richiamare la funzione resetMemory
 */
PvehiclePath __directionMemory__;

// ~~~~~~~~~~~~~~~~~~~~ COSTRUTTORI  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PvehiclePath initVehiclePathCustomized(PvehiclePath _prev, PedgeSol _edge, PvehiclePath _next){
	PvehiclePath result=(PvehiclePath)malloc(sizeof(VehiclePath));
	checkMalloc(result,"solution.c:initVehiclePath:1");
	//creazione della sentinella
	result->edge=_edge;
	result->next=_next;
	result->prev=_prev;
	return result;
}

PvehiclePath initVehiclePath(){
	PvehiclePath result=(PvehiclePath)malloc(sizeof(VehiclePath));
	checkMalloc(result,"solution.c:initVehiclePath:1");
	//creazione della sentinella, bool printMarked
	result->edge=NULL;
	result->next=result;
	result->prev=result;
	return result;
}

void freeVehiclePath(PvehiclePath path){
	free(path);
}

void freeVehiclePathHard(PvehiclePath path){
	PvehiclePath current=path;
	//elimino i successivi path
	while ((current=current->next)->edge!=NULL){
		freeEdgeSol(current->edge);
		free(current);
	}
	//elimino path (aka la sentinella)
	free(path);
}

void freeVehiclePathLight(PvehiclePath path){
	PvehiclePath current=path;
	//elimino i successivi path
	while ((current=current->next)->edge!=NULL){
		free(current);
	}
	//elimino path (aka la sentinella)
	free(path);
}
//TODO testing
PvehiclePath cloneVehiclePath(PvehiclePath path){
	PvehiclePath result=malloc(sizeof(VehiclePath));
	checkMalloc(result,"vehiclePath:cloneVehiclePath:1");
	result->next=path->next;
	result->prev=path->prev;
	result->edge=initEdgeSol(path->edge->id,path->edge->x);
	return result;
}

PvehiclePath cloneVehiclePathHard(PvehiclePath path){
	PvehiclePath result=initVehiclePath();
	PvehiclePath tmp=path;
	PedgeSol e;
	while ((tmp=tmp->next)->edge!=NULL){
		e = cloneEdgeSol(tmp->edge);
		addEdgeSolInVehiclePathCustomized(result,e);
	}
	return result;
}

// ~~~~~~~~~~~~~~~~~~~~ QUERY  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

PedgeSol getEdgeWalkedInVehiclePath(PedgeID edge, PvehiclePath pvp){
	PvehiclePath current = pvp->next;
	while (current->edge!=NULL && !equals(current->edge->id,edge))
		current=current->next;
	if (current->edge==NULL)
		//se current punta alla sentinella allora non hai trovato nulla
		return NULL;
	return current->edge;
}

int getEdgeWalkedTimesInVehiclePath(PedgeID pid, PvehiclePath pvp){
	PedgeSol result = getEdgeWalkedInVehiclePath(pid, pvp);
	return result!=NULL?
			result->x:
			NOT_PASSED;
}

int* getNodeSetTouchedInVehiclePath(PvehiclePath pvp, int N, int* setLength) {
	int v;
	int i = 0;
	int* set = (int*)malloc(N*sizeof(int));
	for(v=0; v<N; v++)
		if (isNodeTouchedInVehiclePath(pvp, v)) {
			set[i] = v;
			i++;
		}
	*setLength = i;
	return set;
}

bool isEdgeWalkedOnceInVehiclePath(PedgeID pid, PvehiclePath pvp) {
	return getEdgeWalkedTimesInVehiclePath(pid, pvp)==1;
}

PedgeSol getNextEdgeInVehiclePath(PvehiclePath pvp){
	if (__directionMemory__->edge==NULL){//ho terminato il percorso
		return NULL;
	}
	PedgeSol result=__directionMemory__->edge;
	__directionMemory__=__directionMemory__->next;
	return result;
}

PedgeSol getPrevEdgeInVehiclePath(PvehiclePath pvp){
	if (__directionMemory__->edge==NULL){//ho terminato il percorso
			return NULL;
	}
	PedgeSol result=__directionMemory__->edge;
	__directionMemory__=__directionMemory__->prev;
	return result;
}

PedgeSol getNearEdgeInVehiclePath(PvehiclePath pvp, int direction){
	if (__directionMemory__->edge==NULL){//ho terminato il percorso
		return NULL;
	}
	PedgeSol result =__directionMemory__->edge;
	switch (direction){
	case DIRECTION_NEXT:{
		__directionMemory__=__directionMemory__->next;
		break;
	}
	case DIRECTION_PREV:{
		__directionMemory__=__directionMemory__->prev;
	}
	}
	return result;
}

bool isEdgeWalkedInVehiclePath(PedgeID edge, PvehiclePath pvp){
	return getEdgeWalkedInVehiclePath(edge,pvp)!=NULL;
}

// ~~~~~~~~~~~~~~~~~~~~ OPERAZIONI  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

bool addEdgeToVehiclePath(PedgeID pid, PvehiclePath pvp){
	//controllo se l'edge e' gia' dentro al percorso
	PvehiclePath current = pvp->next;
	while(current->edge!=NULL){
		if (equals(current->edge->id, pid)){//trovato l'arco, aumento il campo x
			current->edge->x++;
			return false;
		}
		current=current->next;
	}
	//ho ciclato su tutti i nodi e non ho trovato nulla, inserisco in coda
	PvehiclePath new = initVehiclePathCustomized(pvp->prev, initEdgeSol(pid, 1), pvp);
	pvp->prev->next = new;
	pvp->prev = new;
	return true;
}

int removeEdgeFromVehiclePath(PedgeID pid, PvehiclePath pvp){
	//controlle se l'edge e' gia' dentro al percorso
	int result;
	PvehiclePath current = pvp->next;
	while (current->edge!=NULL){
		if (equals(current->edge->id, pid)){//trovato l'arco
			current->edge->x--;
			result=current->edge->x;
			if (current->edge->x==0){//devo eliminare l'arco
				current->prev->next=current->next;
				current->next->prev=current->prev;
				freeVehiclePath(current);
				return EDGE_REMOVED;
			}
			return result;
		}
		current=current->next;
	}
	return EDGE_NOT_FOUND; //non ho trovato nulla. non faccio niente
}
//TODO testing
bool deleteEdgeFromVehiclePath(PedgeID pid, PvehiclePath pvp){
	//controlle se l'edge e' gia' dentro al percorso
	int result;
	PvehiclePath current = pvp->next;
	while (current->edge!=NULL){
		if (equals(current->edge->id, pid)){//trovato l'arco
			current->prev->next=current->next;
			current->next->prev=current->prev;
			freeVehiclePath(current);
			break;
		}
		current=current->next;
	}
	return result; //non ho trovato nulla. non faccio niente
}

void resetMemory(PvehiclePath pvp, int direction){

	switch (direction){
	case DIRECTION_PREV:{
		__directionMemory__ = pvp->prev;
		return;
	}
	case DIRECTION_NEXT: {
		__directionMemory__ = pvp->next;
		return;
	}
	}
	return;
}

bool isNodeTouchedInVehiclePath(PvehiclePath pvp, int v) {

	PvehiclePath current = pvp->next;

	while (current->edge!=NULL){
		if ( current->edge->id->i == v || current->edge->id->j == v)
			return true;
		current = current->next;
	}
	return false;
}

//TODO provare ad inserire questa funzaddEdgeIDInVehiclePathCustomizedione in addEdgeIDInVehiclePath per migliorare la manuntenibilita' del codice
void addEdgeSolInVehiclePathCustomized(PvehiclePath pvp, PedgeSol e){
	PvehiclePath new = initVehiclePathCustomized(pvp->prev, e, pvp);
	pvp->prev->next = new;
	pvp->prev = new;
}

PvehiclePath getEdgesNearNodeInVehiclePath(PvehiclePath pvp, int node){
	PvehiclePath current = pvp;
	PvehiclePath result = initVehiclePath();
	while ((current=current->next)->edge!=NULL){
		if (isNodeUsedByEdgeID(current->edge->id,node)){
			//il nodo e' utilizzato. Metto in result un vehiclePath tale che abbia come edge proprio l'edge di path e i puntatori li aggiusto io
			addEdgeSolInVehiclePathCustomized(result,current->edge);
		}
	}
	return result;
}


// ~~~~~~~~~~~~~~~~~~~~ FUNZIONI DI STAMPA ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void printVehiclePath(FILE* f, PvehiclePath pvp){

	PvehiclePath current = pvp->next;
	fprintf(f, "{");
	while (current->edge!=NULL){
		//fprintf(f,"prev:%p\tcurrent:%p\tnext:%p\n",current->prev,current,current->next);
		printEdgeSol(f, current->edge);
		current = current->next;
	}
	fprintf(f, "}\n");
}
